#region
/* Adoor.NET - Object/Relational mapping framework leveraging ADO.NET`s powerful disconnected mode.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Data;
using System.Xml;
using Adoor.Expression;
using Adoor.Expression.AST;
using Adoor.Object.Domain;
using Adoor.Relational;
using Adoor.Object;
using Adoor.Object.Query;
using Adoor.Object.Entity;
using Adoor.Object.Disconnected;

namespace Adoor.Object.Relational
{
    public enum MappingStrategyEnum { Local, Remote2 }

    public interface IRelationalEntityState
    {
        object this[string name] { get; set; }
        KeyValue Key { get; }
    }

    public interface IEntityMappingStrategyProvider
    {
        IEntityMappingStrategy GetMappingStrategy(MappingStrategyEnum ms);
    }

    public interface IEntityMappingStrategy
    {
        EntityData EntityData { get; }
        RelationalQuery ProperInstancesQuery { get; }
        RelationalQuery AllInstancesQuery { get; }
        ICollection<RelationalQuery> AllInstancesQueryArray { get; }
        ICollection<EntityData> AllChildsArray { get; }
        // TODO: We can have several IEntityMappingStrategy per EntityData, should we have as many KeyMappingStategies or only one ?
        IKeyMappingStrategy KeyMappingStrategy { get; }
    }

    public class EntityTableFactory<TEntity> : RelationalEntityFactory<TEntity>, IEntityFactory, IEntityUpdater, IEntityLoader
    {
        // "select distinct" is possible only if every column allows it
        // otherwise we have to emulate "select distinct" with a "select distinct" on the key only followed by a join on other columns
        private enum DistinctMode { Supported, Emulated }
        private DistinctMode distinctMode;

        private Type implementationType;

        public IOidFactory<TEntity> OidFactory
        {
            get { return (IOidFactory<TEntity>)this.EntityData.OidData.GetService(typeof(IOidFactory)); }
        }

        public EntityTableFactory(EntityData entityData, Type implementationType, XmlNode metadata, XmlNamespaceManager nsmgr)
            :
            base(entityData, metadata, nsmgr)
        {
            this.implementationType = implementationType;
            if (this.implementationType != null)
            {
                if (this.EntityData.IsAbstract)
                    throw new Exception("Abstract entity " + this.EntityData.EntityType.FullName + " should not have an implementation type");
                this.constructorInfo = this.ImplementationType.GetConstructor(BindingFlags.Public |/*BindingFlags.NonPublic |*/ BindingFlags.Instance,
                    null, new Type[] { typeof(IEntityState), typeof(object[]) }, null);
                if (this.constructorInfo == null)
                    throw new Exception("Class " + ImplementationType.Name + " has no protected constructor");
                FieldInfo fi = this.ImplementationType.GetField("Factory");
                if (fi != null)
                    fi.SetValue(null, this);
            }
            else
            {
                if (!this.EntityData.IsAbstract)
                    throw new Exception("Concrete entity " + this.EntityData.EntityType.FullName + " should an implementation type");
            }

            XmlNode tableNameNode = metadata.SelectSingleNode("mapping:entity/@table", nsmgr);
            if (tableNameNode == null)
            {
                if (!this.EntityData.IsAbstract)
                    throw new Exception("Concrete entity " + this.EntityData.EntityType.FullName + " should have a table name");
                this.tableName = "";
            }
            else
            {
                if (this.EntityData.IsAbstract)
                    throw new Exception("Abstract entity " + this.EntityData.EntityType.FullName + " should not have a table name");
                this.tableName = tableNameNode.Value;
            }
            
            XmlNode updateCommandNode = metadata.SelectSingleNode("mapping:entity/@updateCommand", nsmgr);
            if (updateCommandNode != null)
                this.updateCommand = updateCommandNode.Value;
            
            XmlNode insertCommandNode = metadata.SelectSingleNode("mapping:entity/@insertCommand", nsmgr);
            if (insertCommandNode != null)
                this.insertCommand = insertCommandNode.Value;
            
            XmlNode deleteCommandNode = metadata.SelectSingleNode("mapping:entity/@deleteCommand", nsmgr);
            if (deleteCommandNode != null)
                this.deleteCommand = deleteCommandNode.Value;
            
            XmlNode distinctModeNode = metadata.SelectSingleNode("mapping:entity/@distinctMode", nsmgr);
            
            if (distinctModeNode != null)
                this.distinctMode = (DistinctMode)Enum.Parse(typeof(DistinctMode), distinctModeNode.Value, true);

            if (this.implementationType != null)
            {
                this.EntityData.RegisterService(typeof(IEntityFactory), this);
                this.EntityData.RegisterService(typeof(IEntityUpdater), this);
                //this.EntityData.RegisterService(typeof(IEntityLoader), this);
            }
        }

        public Type ImplementationType
        {
            get { return this.implementationType; }
        }

        private string tableName;
        public string TableName
        {
            get { return this.tableName; }
        }

        public object Create(IObjectStorage storage, params object[] args)
        {
            if (this.EntityData.IsAbstract)
                throw new Exception("Cannot instantiate instances of abstract entity types");
            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;

            DataRow row = rstorage.CreateRow(this.tableName, this.DataColumns);

            EntityState<TEntity> state = new EntityState<TEntity>(row, rstorage, this/*, args*/);
            this.OidFactory.CreateKey(state);

            IEntity result = (IEntity)this.constructorInfo.Invoke(new object[] { state, args });
            //TODO: Caching and ensure one instance per context
            row.Table.ExtendedProperties[row] = result;
            return result;
        }

        internal KeyValue GetKey(EntityState<TEntity> state)
        {
            DataRow row = state.Row;
            return this.KeyMappingStrategy.GetKey(row);
        }

        internal string GetOid(EntityState<TEntity> state)
        {
            return this.KeyMappingStrategy.GetOid(this.KeyMappingStrategy.GetKey(state.Row));
        }

        public IEntity Find(IObjectStorage storage, string oid)
        {
            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;

            DataView index = rstorage.GetIndex(this.tableName, this.KeyMappingStrategy.SortString);
            if (index == null)
                return null;

            if ((index.RowStateFilter & DataViewRowState.Deleted) == 0)
                throw new Exception();
            
            KeyValue key = this.KeyMappingStrategy.ExtractKey(oid);
            int rowIndex = index.Find(key.Values);
            if (rowIndex == -1)
                return null;
            DataRow row = index[rowIndex].Row;
            //TODO: Caching and ensure one instance per context
            IEntity result = (IEntity)row.Table.ExtendedProperties[row];
            if (result == null)
            {
                EntityState<TEntity> state = new EntityState<TEntity>(row, rstorage, this/*, null*/);
                result = (IEntity)this.constructorInfo.Invoke(new object[] { state, null });
                row.Table.ExtendedProperties[row] = result;

                // TODO: call OnMaterialize here
            }
            return result;
        }

        public override void Load(ObjectContext context, string correlation, ObjectPath path, object args)
        {
            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found in the context for domain " + this.EntityData.Domain);
            }

            if(string.IsNullOrEmpty(correlation))
                throw new Exception(" correlation cannot be null or empty ");

            object rargs = PreloadQueryGenerator.GenerateArgs(args);

            RelationalOperator rq = PreloadQueryGenerator.GenerateOperator(MappingStrategyEnum.Remote2, path, correlation, this.EntityData);

            List<SelectExpression> columns = new List<SelectExpression>();
            foreach (DataColumn dc in this.DataColumns)
                columns.Add(new SelectExpression(dc.ColumnName, new ExpressionName(correlation, dc.ColumnName)));

            RelationalQuery rq2;
            if (this.distinctMode == DistinctMode.Supported)
            {
                rq2 = rq.Project(columns);
                rq2.Distinct = true;
            }
            else
            {
                Correlation c = (Correlation)path.Correlations[correlation];
                if (c == null)
                    throw new Exception("Correlation " + correlation + " not found in the ObjectPath");
                IEntityMappingStrategy remoteMappingStrategy = this.GetMappingStrategy(MappingStrategyEnum.Remote2);
                rq2 = remoteMappingStrategy.KeyMappingStrategy.Project(rq, correlation);

                rq2.Distinct = true;
                rq = new AliasOperator(rq2, "t_1");
                string primaryKey = remoteMappingStrategy.KeyMappingStrategy.PrimaryKey;
                rq = rq.Join(false, primaryKey, remoteMappingStrategy.AllInstancesQuery,
                    primaryKey, "t_1", correlation);
                rq2 = rq.Project(columns);
            }

            DataTable table = Adoor.Data.DataUtils.CreateCaseInsesitiveDataTable();
            remoteSource.RelationalSource.Fill(rq2, rargs, table);  // TODO: use a DataReader

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            DataView dv = new DataView(table);
            
            storage.FillTable(this.tableName, this.DataColumns, dv);

            base.Load(context, correlation, path, args);
        }

        public override void Load(ObjectContext context, string oid)
        {
            string entityName = this.EntityData.EntityType.Name;
            ObjectPath path = this.EntityData.GetPath(
                entityName
                ).Where("[" + entityName + "] = ?");

            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found in the context for domain " + this.EntityData.Domain);
            }

            KeyValue key = this.KeyMappingStrategy.ExtractKey(oid);

            System.Collections.ICollection args = RelationalQueryGenerator.GenerateArgs(new object[] { key });
            RelationalOperator rq = RelationalQueryGenerator.GenerateOperator(MappingStrategyEnum.Remote2, path);

            DataTable table = Adoor.Data.DataUtils.CreateCaseInsesitiveDataTable();
            remoteSource.RelationalSource.Fill(rq.AsQuery(), args, table);

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            storage.FillTable(this.tableName, this.DataColumns, new DataView(table));
        }

        public void Clear(IObjectStorage storage)
        {
            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;

            DataTable table = rstorage.GetTable(this.tableName, null);
            if (table == null)
                return;
            table.ExtendedProperties.Clear();
            table.Clear();
            foreach (ReferenceData referenceData in this.EntityData.ReferenceInfos.Values)
            {
                IReferenceWriter writer = (IReferenceWriter)referenceData.GetService(typeof(IReferenceWriter));
                if (writer != null)
                    writer.Clear(storage);
            }
        }


        public void Cancel(IObjectStorage storage)
        {
            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;

            DataTable table = rstorage.GetTable(this.tableName, null);
            if (table == null)
                return;
            table.RejectChanges();
            foreach (ReferenceData referenceData in this.EntityData.ReferenceInfos.Values)
            {
                IReferenceWriter writer = (IReferenceWriter)referenceData.GetService(typeof(IReferenceWriter));
                if (writer != null)
                    writer.Cancel(storage);
            }
        }

        public void Validate(IObjectStorage storage, Adoor.Object.Disconnected.ObjectContext context)
        {
            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;

            DataTable table = rstorage.GetTable(this.tableName, null);
            if (table == null)
                return;
            table.ExtendedProperties["Updated"] = false;
            table.ExtendedProperties["Inserted"] = false;
            table.ExtendedProperties["Deleted"] = false;
            this.RaiseValidate(table, context);
        }

        public void UpdateDeletedEntities(ObjectContext context)
        {
            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found for domain " + this.EntityData.Domain.Name);
            }

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            DataTable table = storage.GetTable(this.tableName, null);
            if (table == null)
                return;
            System.Threading.Monitor.Enter(table);
            try
            {

                if (table.ExtendedProperties.ContainsKey("Deleted") 
                    && Convert.ToBoolean(table.ExtendedProperties["Deleted"]))
                    return;

                table.ExtendedProperties["Deleted"] = true;

                remoteSource.RelationalSource.UpdateTableDelete(table, this.deleteCommand, this.DataColumns);
            }
            finally
            {
                System.Threading.Monitor.Exit(table);
            }
        }
        public void UpdateInsertEntities(ObjectContext context)
        {
            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found for domain " + this.EntityData.Domain.Name);
            }

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            DataTable table = storage.GetTable(this.tableName, null);
            if (table == null)
                return;
            System.Threading.Monitor.Enter(table);
            try
            {

                //foreach (DependantEntity dep in this.EntityData.Domain.ObjectDependencies[this.EntityData])
                //{
                //    IEntityUpdater updater = (IEntityUpdater)rm.TargetData.GetService(typeof(IEntityUpdater));
                //    if (updater != null)
                //        updater.UpdateEntities(idTransaction, context);

                //    remoteSource.RelationalSource.UpdateTableDelete(table, this.GetMappingStrategy(MappingStrategyEnum.Remote2).KeyMappingStrategy.Identity,
                //    this.updateCommand, this.insertCommand, this.deleteCommand, this.DataColumns);
                //}

                if (Convert.ToBoolean(table.ExtendedProperties["Inserted"]))
                    return;
                table.ExtendedProperties["Inserted"] = true;

                //this.EntityData.RaiseUpdateEvent(remoteSource.GetTransaction(idTransaction));
                remoteSource.RelationalSource.UpdateTableInsert(table, this.insertCommand, this.DataColumns);
            }
            finally
            {
                System.Threading.Monitor.Exit(table);
            }
        }

        public void UpdateUpdatedEntities(ObjectContext context)
        {
            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found for domain " + this.EntityData.Domain.Name);
            }

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            DataTable table = storage.GetTable(this.tableName, null);
            if (table == null)
                return;
            System.Threading.Monitor.Enter(table);
            try
            {

                //foreach (DependantEntity dep in this.EntityData.Domain.ObjectDependencies[this.EntityData])
                //{
                //    IEntityUpdater updater = (IEntityUpdater)rm.TargetData.GetService(typeof(IEntityUpdater));
                //    if (updater != null)
                //        updater.UpdateEntities(idTransaction, context);

                //    remoteSource.RelationalSource.UpdateTableDelete(table, this.GetMappingStrategy(MappingStrategyEnum.Remote2).KeyMappingStrategy.Identity,
                //    this.updateCommand, this.insertCommand, this.deleteCommand, this.DataColumns);
                //}

                if (Convert.ToBoolean(table.ExtendedProperties["Updated"]))
                    return;
                table.ExtendedProperties["Updated"] = true;

                //this.EntityData.RaiseUpdateEvent(remoteSource.GetTransaction(idTransaction));
                remoteSource.RelationalSource.UpdateTableUpdate(table, this.updateCommand, this.DataColumns);
            }
            finally
            {
                System.Threading.Monitor.Exit(table);
            }
        }

        public void Commit(ObjectContext context)
        {
            RelationalObjectSource remoteSource = (RelationalObjectSource)this.EntityData.Domain.DomainGroup.RemoteSource;
            if (remoteSource == null)
            {
                throw new Exception("Remote source not found for domain " + this.EntityData.Domain.Name);
            }

            // TODO: remove cast (use generics in C# 2)
            DataSetObjectStorage storage = (DataSetObjectStorage)context.LocalStorage;

            DataTable table = storage.GetTable(this.tableName, null);
            if (table == null)
                return;
            table.AcceptChanges();

            foreach (ReferenceData referenceData in this.EntityData.ReferenceInfos.Values)
            {
                IReferenceWriter writer = (IReferenceWriter)referenceData.GetService(typeof(IReferenceWriter));
                if(writer == null)
                    throw new Exception("Unable to find IReferenceWriter in " + referenceData.TargetRole + " - " + referenceData.InverseRole);
                if (writer != null)
                    writer.Commit(context);
            }
        }

        public void UpdateRelations(ObjectContext context)
        {
            foreach (ReferenceData referenceData in this.EntityData.ReferenceInfos.Values)
            {
                IReferenceWriter writer = (IReferenceWriter)referenceData.GetService(typeof(IReferenceWriter));
                if(writer == null)
                    throw new Exception("Unable to find IReferenceWriter in " + referenceData.TargetRole + " - " + referenceData.InverseRole);
                if (writer != null)
                    writer.Update(context);
            }
        }

        internal bool NeedsPreloading(EntityState<TEntity> state, string roleName, ref PreloadFlags flags)
        {
            if (state.Status == EntityStatus.Added)
                return false;

            KeyValue key = this.GetKey(state);

            // TODO: remove cast (use generics in C# 2)

            DataSetObjectStorage storage = (DataSetObjectStorage)state.Storage;

            DataView index = storage.GetIndex(this.tableName, this.KeyMappingStrategy.SortString);
            
            int i = index.Find(key.Values);
            
            EntityRow row = (EntityRow)index[i].Row;
            if (row.RowState == DataRowState.Deleted)
                return false;

            PreloadFlags cflags = (PreloadFlags)row.GetExtendedProperty(roleName);
            flags &= ~cflags;
            return flags != PreloadFlags.None;
        }

        public override void CompleteReference(IObjectStorage storage, string oid, string roleName, PreloadFlags flags)
        {
            EntityData entityData = this.EntityData.GetActualEntityData(oid);
            if (entityData != this.EntityData)
            {
                IEntityLoader loader = (IEntityLoader)entityData.GetService(typeof(IEntityLoader));
                loader.CompleteReference(storage, oid, roleName, flags);
                return;
            }

            DataSetObjectStorage rstorage = (DataSetObjectStorage)storage;
            DataView index = rstorage.GetIndex(this.tableName, this.KeyMappingStrategy.SortString);
            KeyValue key = this.KeyMappingStrategy.ExtractKey(oid);
            int i = index.Find(key.Values);
            
            EntityRow row = (EntityRow)index[i].Row;
            DataRowState rowState = row.RowState;
            if (rowState == DataRowState.Deleted)
                return;

            PreloadFlags cflags = (PreloadFlags)row.GetExtendedProperty(roleName);
            cflags |= flags;
            row.SetExtendedProperty(roleName, (int)cflags);
        }

        public override RelationalQuery ProperInstancesQuery
        {
            get
            {
                //Clone dataColumns
                List<DataColumn> newCols = new List<DataColumn>();
                foreach (DataColumn o in this.DataColumns)
                {
                    DataColumn dc = (DataColumn)o;
                    DataColumn newDc = new DataColumn();
                    newDc.AllowDBNull = dc.AllowDBNull;
                    newDc.Caption = dc.Caption;
                    newDc.ColumnName = dc.ColumnName;
                    newDc.DataType = dc.DataType;
                    newDc.DefaultValue = dc.DefaultValue;
                    newDc.Expression = dc.Expression;
                    newDc.MaxLength = dc.MaxLength;
                    newDc.Unique = dc.Unique;
                    newCols.Add(newDc);
                }

                return this.TableName == "" ? null : new ScanQuery(this.TableName, newCols);
            }
        }


        private ConstructorInfo constructorInfo;
        private string updateCommand;
        private string insertCommand;
        private string deleteCommand;

        public static event ValidateEventHandler ValidateEntity;

        public void RaiseValidate(System.Data.DataTable dt, ObjectContext context)
        {
            if (ValidateEntity != null)
                ValidateEntity(this, new ValidateEventArgs(this.EntityData, dt, context));
        }
    }

    class EntityState<T> : IEntityState, IRelationalEntityState
    {
        public EntityState(DataRow row, DataSetObjectStorage storage, EntityTableFactory<T> factory/*, object[] args*/)
        {
            this.row = row;
            this.factory = factory;
            this.storage = storage;
        }

        public IObjectStorage Storage
        {
            get { return this.storage; }
        }

        public DataRow Row
        {
            get { return this.row; }
        }

        public IEntityFactory EntityFactory
        {
            get { return this.factory; }
        }

        public EntityStatus Status
        {
            get
            {
                switch (this.Row.RowState)
                {
                    case DataRowState.Added:
                        return EntityStatus.Added;
                    case DataRowState.Deleted:
                        return EntityStatus.Deleted;
                    case DataRowState.Modified:
                        return EntityStatus.Modified;
                    case DataRowState.Unchanged:
                        return EntityStatus.Unchanged;
                    default:
                        return EntityStatus.Unchanged;
                }
            }
        }

        public string Oid
        {
            get
            {
                return this.factory.KeyMappingStrategy.GetOid(this.Key);
            }
        }

        public KeyValue Key
        {
            get
            {
                return this.factory.GetKey(this);
            }
        }

        public void Touch()
        {
            this.row[0] = this.row[0]; // DataRowState = modified
        }

        public void Delete()
        {
            //			foreach(IStoredReferenceFactory relfactory in this.factory.ReferenceInfos)
            //			{
            //				if (relfactory.Aggregate) 
            //				{
            //					//					if (relfactory is IReferenceFactory) // TODO: remove this c5_1 (factor out IReferenceFactory and IListFactory)
            //					//					{
            //					//						IEntity entity = (IEntity)((IReferenceFactory)relfactory).Fetch((IEntity)this.Entity);
            //					//						if (entity != null)
            //					//							this.Context.Delete(entity);
            //					//					}
            //					//					else 
            //					//					{
            //					//						foreach(IEntity entity in ((IListFactory)relfactory).Collect((IEntity)this.Entity))
            //					//							this.Context.Delete(entity);
            //					//					}
            //					foreach(IEntity entity in relfactory.Collect(this))
            //						this.Context.LocalStore.Delete(entity);
            //
            //				}
            //			}
            this.row.Delete();
        }

        public object this[string name]
        {
            get { return this.row[name]; }
            set { this.row[name] = value; }
        }

        public bool IsReferenceComplete(string roleName, ref PreloadFlags flags)
        {
            return !this.factory.NeedsPreloading(this, roleName, ref flags);
        }

        public void CompleteReference(string roleName, PreloadFlags flags)
        {
            this.factory.CompleteReference(this.Storage, this.Oid, roleName, flags);
        }

        private DataRow row;
        private EntityTableFactory<T> factory;
        private DataSetObjectStorage storage;
    }

    public delegate void ValidateEventHandler(object sender, ValidateEventArgs e);
    public class ValidateEventArgs : EventArgs
    {
        public ValidateEventArgs(EntityData ed, System.Data.DataTable dt, ObjectContext context)
        {
            this.entityData = ed;
            this.entityTable = dt;
            this.context = context;
        }

        private EntityData entityData;
        public EntityData EntityData
        {
            get { return entityData; }
        }

        private DataTable entityTable;
        public DataTable EntityTable
        {
            get { return entityTable; }
        }

        private ObjectContext context;
        public ObjectContext Context
        {
            get { return context; }
        }
    }
}
